/*
	Written By Sanjay Giri and Pradipna Nepal
	www.pradsprojects.com
	
	Copyright (C) 2012 Prads Projects
	Please read COPYING.txt included along with this source code for more detail.
	If not included, see http://www.gnu.org/licenses/
*/

#include "libTokenizer.h"


Tokenizer::Tokenizer(string filename) {
	ifstream file(filename.c_str());

	if(file.good()) {
		string temp = "";
		string accum = "";
		bool slash = false;

		while(getline(file, temp)) {
			for (int i = 0; i < temp.size(); ++i) {
				if (temp[i] == '/') {
					if (slash) {	//Comment
						slash = false;
						break;
					} else {
						slash = true;
					}
				} else {
					if (slash) {
						accum += '/';
						slash = false;
					}
					accum += temp[i];
				}
			}
			accum += " ";
		}

		data = accum;
	} else {
		cout << "File didnt open..." << endl;
	}
}


TokenDescription Tokenizer::isKeyWord(string accum) {
	if (accum == "void") 
		return KW_VOID;
	else if(accum == "for")
		return KW_FOR;
	else if(accum == "var")
		return KW_VAR;
	else if(accum == "const")
		return KW_CONST;
	else if(accum == "if")
		return KW_IF;
	else if(accum == "else")
		return KW_ELSE;
	else if(accum == "while")
		return KW_WHILE;
	else if(accum == "struct")
		return KW_STRUCT;
	else if(accum == "switch")
		return KW_SWITCH;
	else if(accum == "case")
		return KW_CASE;
	else if(accum == "default")
		return KW_DEFAULT;
	else if(accum == "break")
		return KW_BREAK;
	else if(accum == "continue")
		return KW_CONTINUE;
	else if(accum == "ConstString")
		return KW_CONSTSTRING;
	else if(accum == "goto")
		return KW_GOTO;
	else if(accum == "return")
		return KW_RETURN;
	else if(accum == "main")
		return KW_MAIN;
	else if (accum == "SetBackground")
		return KW_BGC_N;
	else if (accum == "Print")
		return KW_PRINT;
	else if (accum == "Image")
		return KW_IMAGE;
	else if (accum == "Draw")
		return KW_DRAW;
	else if (accum == "ClearScreen")
		return KW_CLS;
	else if (accum == "GetController1")
		return KW_CTRL1;
	else if (accum == "WaitScreenRefresh")
		return KW_VBLANK;
	else if (accum == "Rand")
		return KW_RANDOM;
	else if (accum == "Flip")
		return KW_FLIP;
	else 
		return DESC_NONE;
	
}

vector<Token> Tokenizer::tokenize() {
	vector<Token> list;
	int index = 0;
	string accum = "";
	int length = data.size();

	try {
		while (index < length) {
			if (isspace(data[index])) {
				accum = "";
				index++;
			} else if (data[index] == '"') {
				index++;

				while (data[index] != '"') {
					if (index == (data.size() - 1)) { 
						throw 10; 
					}
						
					accum += data[index++];
				}
				index++;
				list.push_back(Token(accum, TOKEN_STRING, DESC_NONE));

				accum = "";

			} else if (isalpha(data[index])) {
				accum += data[index];
				index++;

				while (1) {
					if (isalnum(data[index]) || data[index] == '_') {
						accum += data[index++];
					} else {
						break;
					}
				}
			
				TokenDescription kwType = isKeyWord(accum);
				if (kwType == DESC_NONE) {
					list.push_back(Token(accum, TOKEN_IDENTIFIER, DESC_NONE));
				} else {
					list.push_back(Token(accum, TOKEN_KEYWORD, kwType));
				}

				accum = "";
			} else if (isdigit(data[index])) {
				accum += data[index];
				index++;

				while (isdigit(data[index])){
					accum += data[index];
					index++;	
				}

				if (isalpha(data[index])) {
					throw 11;
				}

				list.push_back(Token(accum, TOKEN_DIGIT, DESC_NONE));

				accum = "";
			} else if (data[index] == '+') {
				accum += data[index];
				list.push_back(Token(accum, TOKEN_OPERATOR, OPERATOR_ADD));
				accum = "";
				index++;
			} else if (data[index] == '-') {
				accum += data[index];
				index++;
				if(isdigit(data[index])) {
					accum += data[index];
					index++;

					while (isdigit(data[index])){
						accum += data[index];
						index++;	
					}

					if (isalpha(data[index])) {
						throw 11;
					}

					list.push_back(Token(accum, TOKEN_DIGIT, DESC_NONE));

					accum = "";
				} else {
					list.push_back(Token(accum, TOKEN_OPERATOR, OPERATOR_SUB));
					accum = "";
					index++;
	
				}
			} else if (data[index] == '*') {
				accum += data[index];
				list.push_back(Token(accum, TOKEN_OPERATOR, OPERATOR_MUL));
				accum = "";
				index++;
			} else if (data[index] == '/') {
				accum += data[index];
				list.push_back(Token(accum, TOKEN_OPERATOR, OPERATOR_DIV));
				accum = "";
				index++;
			} else if (data[index] == '%') {
				accum += data[index];
				list.push_back(Token(accum, TOKEN_OPERATOR, OPERATOR_MODULO));
				accum = "";
				index++;
			} else if (data[index] == '&') {
				if(data[++index] == '&') {
					list.push_back(Token(accum, TOKEN_OPERATOR, OPERATOR_LOG_AND));
					accum = "";
					index++;
				} else {
					list.push_back(Token(accum, TOKEN_OPERATOR, OPERATOR_BIT_AND));
					accum = "";
				}
			} else if (data[index] == '|') {
				if(data[++index] == '|') {
					list.push_back(Token(accum, TOKEN_OPERATOR, OPERATOR_LOG_OR));
					accum = "";
					index++;
				} else {
					list.push_back(Token(accum, TOKEN_OPERATOR, OPERATOR_BIT_OR));
					accum = "";
				}
			} else if (data[index] == '^') {
				accum += data[index];
				list.push_back(Token(accum, TOKEN_OPERATOR, OPERATOR_MODULO));
				accum = "";
				index++;
			} else if (data[index] == '=') {
				
				if (data[++index] == '=') {
					list.push_back(Token("==", TOKEN_COMPARE, COMPARE_EQUALS));
					accum = "";
					index++;
				} else {
					list.push_back(Token("=", TOKEN_OPERATOR, OPERATOR_ASSIGN));
					accum = "";
				}
			} else if (data[index] == '!') {
				if (data[++index] == '=') {
					list.push_back(Token("!=", TOKEN_OPERATOR, OPERATOR_ASSIGN));
					accum = "";
					++index;
				} else {
					throw 12;
				}
			} else if (data[index] == '<'){
				if (data[++index] == '<'){
					list.push_back(Token("<<", TOKEN_OPERATOR, OPERATOR_SHIFT_LEFT));
					accum = "";
					index++;
				} else if (data[index] == '=') {
					list.push_back(Token("<=", TOKEN_COMPARE, COMPARE_LESS_EQUAL));
					accum = "";
					index++;
				} else {
					list.push_back(Token("<", TOKEN_COMPARE, COMPARE_LESS));
					accum = "";
				}
			} else if (data[index] == '>') {
				if (data[++index] == '>') {
					list.push_back(Token(">>", TOKEN_OPERATOR, OPERATOR_SHIFT_RIGHT));
					accum = "";
					index++;
				} else if (data[index] == '=') {
					list.push_back(Token(">=", TOKEN_COMPARE, COMPARE_GREATER_EQUAL));
					accum = "";
					index++;
				} else {
					list.push_back(Token(">", TOKEN_COMPARE, COMPARE_GREATER));
					accum = "";
				}
			} else if (data[index] == ';') {
				accum += data[index];
				list.push_back(Token(accum, TOKEN_SEMICOLON, DESC_NONE));
				accum = "";
				index++;
			} else if (data[index] == '(') {
				accum += data[index];
				list.push_back(Token(accum, TOKEN_OPENROUNDBRACKET, DESC_NONE));
				accum = "";
				index++;
			} else if(data[index] == ')') {
				accum += data[index];
				list.push_back(Token(accum, TOKEN_CLOSEROUNDBRACKET, DESC_NONE));
				accum = "";
				index++;
			} else if (data[index] == '.') {
				accum += data[index];
				list.push_back(Token(accum, TOKEN_PERIOD, DESC_NONE));
				accum = "";
				index++;
			} else if (data[index] == '{') {
				accum += data[index];
				list.push_back(Token(accum, TOKEN_OPENCURLYBRACKET, DESC_NONE));
				accum = "";
				index++;
			} else if (data[index] == '}') {
				accum += data[index];
				list.push_back(Token(accum, TOKEN_CLOSECURLYBRACKET, DESC_NONE));
				accum = "";
				index++;
			} else if (data[index] == '[') {
				accum += data[index];
				list.push_back(Token(accum, TOKEN_OPENSQUAREBRACKET, DESC_NONE));
				accum = "";
				index++;
			} else if (data[index] == ']') {
				accum += data[index];
				list.push_back(Token(accum, TOKEN_CLOSESQUAREBRACKET, DESC_NONE));
				accum = "";
				index++;
			} else if (data[index] == ',') {
				accum += data[index];
				list.push_back(Token(accum, TOKEN_COMMA, DESC_NONE));
				accum = "";
				index++;
			} else {
				throw 12;
			}
		}
	} catch (int i) {
		if(i == 10) {
			cout << "You forgot to close the string" <<endl;
			list.clear();
		}
		else if(i == 11) {
			cout << "identifier names cannot start with number" <<endl;
			list.clear();
		} else if (i == 12) {
			cout << "Illegal Character: ";
			cout << data[index] << endl;
			list.clear();
		}
	}

	return list;
}